/*****************************************************************************/
/* CS 230 Final Project                                                      */
/* SocialNetworkSimulation.cpp                                               */
/*                                                                           */
/* Created by Even Cheng                                                     */
/* Copyright 2011 GNU GPL v3                                                 */
/*****************************************************************************/

#include "stdafx.h"
#include "SocialNetworkSimulation.h"
#include <iostream>
#include <fstream>
#include <time.h>
#include "graph.h"
#include <Windows.h>
#include "events.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name

int timeNow;
int timeHistory[105 / 5];						// we take 5% as one step in recording information and discount the first 5%
char buffer[100];

graph sim;
neighbor *index;
float xExpansionRatio = DISPLAY_STRETCH_X,
	  yExpansionRatio = DISPLAY_STRETCH_Y;

events queue;

HDC hdcMem;
HBITMAP hbmMem;
HANDLE       hOld;

RECT rc;

node sender, recipient;

HPEN hWhiteLineThickPen, hBlackLineThickPen, hWhiteLineThinPen, hBlackLineThinPen,
	hGrayLineThinPen, hGrayLineThickPen, hRedLineThickPen, hGreenLineThickPen, hRedLineThinPen, hGreenLineThinPen,
	hYellowLineThickPen, hYellowLineThinPen, hPurpleLineThinPen,
	hBlueLineThickPen, hBlueLineThinPen, hPenOld, hLightGrayLineThickPen, hLightGrayLineThinPen,
	hOrangeLineThickPen, hOrangeLineThinPen;
COLORREF qWhiteLine, qGrayLine, qBlackLine, qRedLine, qGreenLine, qYellowLine, qBlueLine, qLightGrayLine, qOrangeLine, qPurpleLine;

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
MSG msg;
bool gbDrawLine     = false;
bool gbDrawEllipse  = false;

using namespace std;

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: Place code here.
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_SOCIALNETWORKSIMULATION, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SOCIALNETWORKSIMULATION));

	int nodes_number = MAX_NODE_NUMBER;
	int number = 0;
	int con = 1;
	int clock = 0;
	int flag;
	int node = 0;
	
	sim.Readmap();
	sim.humancontact();
	queue.sort();

	for(clock=0;clock<=MAX_RUNNING_TIME;clock++)
	{
		if(node > MAX_NODE_NUMBER)	continue;

		while(queue.head!= NULL && clock >= queue.head->clock)
		{
			flag = queue.simulate(&sim, clock, &node);

			if(flag == 1 || flag == 0)
			{
				sim.nodes[node].on = flag;
			}
		}
		if (GetMessage(&msg, NULL, 0, 0))
		{
			if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
			{
				timeNow = clock;
				TranslateMessage(&msg);
				DispatchMessage(&msg);
				InvalidateRect(msg.hwnd, 0, FALSE);
			}
		}
		if(clock<50)
			Sleep(10);
		else
			Sleep(1 * SLOW_DOWN_FACTOR);
	}

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	exit(0);
	return (int) msg.wParam;
	
}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SOCIALNETWORKSIMULATION));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_GRAYTEXT);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_SOCIALNETWORKSIMULATION);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
     10, 10, DISPLAY_WINDOW_WIDTH, DISPLAY_WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   	// initialize the brushes
	qWhiteLine = RGB(255, 255, 255);
	hWhiteLineThickPen = CreatePen(PS_SOLID, 5, qWhiteLine);
	hWhiteLineThinPen = CreatePen(PS_SOLID, 1, qWhiteLine);

	qGrayLine = RGB(110, 110, 110);
	hGrayLineThickPen = CreatePen(PS_SOLID, 5, qGrayLine);
	hGrayLineThinPen = CreatePen(PS_SOLID, 1, qGrayLine);
	
	qLightGrayLine = RGB(128, 128, 128);
	hLightGrayLineThickPen = CreatePen(PS_SOLID, 5, qLightGrayLine);
	hLightGrayLineThinPen = CreatePen(PS_SOLID, 1, qLightGrayLine);

	qBlackLine = RGB(0, 0, 0);
	hBlackLineThickPen = CreatePen(PS_SOLID, 5, qBlackLine);
	hBlackLineThinPen = CreatePen(PS_SOLID, 1, qBlackLine);

	qRedLine = RGB(255, 0, 0);
	hRedLineThickPen = CreatePen(PS_SOLID, 5, qRedLine);
	qRedLine = RGB(255, 120, 120);
	hRedLineThinPen = CreatePen(PS_SOLID, 1, qRedLine);

	qOrangeLine = RGB(255, 64, 0);
	hOrangeLineThickPen = CreatePen(PS_SOLID, 5, qOrangeLine);
	qOrangeLine = RGB(255, 184, 120);
	hOrangeLineThinPen = CreatePen(PS_SOLID, 1, qOrangeLine);

	qYellowLine = RGB(255, 255, 0);
	hYellowLineThickPen = CreatePen(PS_SOLID, 5, qYellowLine);
	qYellowLine = RGB(255, 255, 120);
	hYellowLineThinPen = CreatePen(PS_SOLID, 1, qYellowLine);

	qGreenLine = RGB(0, 255, 0);
	hGreenLineThickPen = CreatePen(PS_SOLID, 5, qGreenLine);
	qGreenLine = RGB(120, 255, 120);
	hGreenLineThinPen = CreatePen(PS_SOLID, 1, qGreenLine);

	qBlueLine = RGB(0, 0, 255);
	hBlueLineThickPen = CreatePen(PS_SOLID, 5, qBlueLine);
	qBlueLine = RGB(120, 120, 255);
	hBlueLineThinPen = CreatePen(PS_SOLID, 1, qBlueLine);

	qPurpleLine = RGB(210, 143, 255);
	hPurpleLineThinPen = CreatePen(PS_SOLID, 1, qPurpleLine);

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}


//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	LPRECT prc = &rc;
	GetClientRect(hWnd, prc);
	

	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			::abort();
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);

		// use double buffering here so the screen will never flicker again
		hdcMem = CreateCompatibleDC(hdc);
		hbmMem = CreateCompatibleBitmap(hdc, DISPLAY_WINDOW_WIDTH, DISPLAY_WINDOW_HEIGHT);
		hOld   = SelectObject(hdcMem, hbmMem);

		// paint the background
		rc.left = 0;
		rc.top = 0;
		rc.right = DISPLAY_WINDOW_WIDTH;
		rc.bottom = DISPLAY_WINDOW_HEIGHT;
		::FillRect(hdcMem, &rc, (HBRUSH)(COLOR_GRAYTEXT));

		// paint log section at the right
		prc->top = 20;
		prc->left = 1100;
		sprintf_s(buffer, 50, "tick now: %d", timeNow);
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 35;
		sprintf_s(buffer, 50, "# of recv msg: %d", queue.nodesReceivedMessage);
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		// draw legends
		prc->top = 75;
		sprintf_s(buffer, 50, "Legends::");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);
		prc->top = 90;
		sprintf_s(buffer, 50, "Node Status::");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);
		
		prc->left = 1120;
		prc->top = 115;
		sprintf_s(buffer, 50, "Offline");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 130;
		sprintf_s(buffer, 50, "Online");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 145;
		sprintf_s(buffer, 50, "Receiving Data");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 160;
		sprintf_s(buffer, 50, "Received Data");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 175;
		sprintf_s(buffer, 50, "Sending Data");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 190;
		sprintf_s(buffer, 50, "Sent Data");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		hPenOld = (HPEN)SelectObject(hdcMem, hBlackLineThickPen);
		::Ellipse(hdcMem, 1105, 121, 1108, 124);

		hPenOld = (HPEN)SelectObject(hdcMem, hWhiteLineThickPen);
		::Ellipse(hdcMem, 1105, 136, 1108, 139);

		hPenOld = (HPEN)SelectObject(hdcMem, hRedLineThickPen);
		::Ellipse(hdcMem, 1105, 151, 1108, 154);

		hPenOld = (HPEN)SelectObject(hdcMem, hOrangeLineThickPen);
		::Ellipse(hdcMem, 1105, 166, 1108, 169);

		hPenOld = (HPEN)SelectObject(hdcMem, hYellowLineThickPen);
		::Ellipse(hdcMem, 1105, 181, 1108, 184);

		hPenOld = (HPEN)SelectObject(hdcMem, hGreenLineThickPen);
		::Ellipse(hdcMem, 1105, 196, 1108, 199);

		// draw legends::lines
		prc->left = 1100;
		prc->top = 220;
		sprintf_s(buffer, 50, "Channel Types::");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->left = 1120;
		prc->top = 245;
		sprintf_s(buffer, 50, "Inactive");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 260;
		sprintf_s(buffer, 50, "Email");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 275;
		sprintf_s(buffer, 50, "OSN");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 290;
		sprintf_s(buffer, 50, "SMS");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 305;
		sprintf_s(buffer, 50, "Phone");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		prc->top = 320;
		sprintf_s(buffer, 50, "Human Contact");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		hPenOld = (HPEN)SelectObject(hdcMem, hGrayLineThinPen);
		::MoveToEx(hdcMem, 1105, 252, NULL);
		::LineTo(hdcMem, 1115, 252);

		hPenOld = (HPEN)SelectObject(hdcMem, hRedLineThinPen);
		::MoveToEx(hdcMem, 1105, 267, NULL);
		::LineTo(hdcMem, 1115, 267);

		hPenOld = (HPEN)SelectObject(hdcMem, hYellowLineThinPen);
		::MoveToEx(hdcMem, 1105, 282, NULL);
		::LineTo(hdcMem, 1115, 282);

		hPenOld = (HPEN)SelectObject(hdcMem, hGreenLineThinPen);
		::MoveToEx(hdcMem, 1105, 297, NULL);
		::LineTo(hdcMem, 1115, 297);

		hPenOld = (HPEN)SelectObject(hdcMem, hBlueLineThinPen);
		::MoveToEx(hdcMem, 1105, 312, NULL);
		::LineTo(hdcMem, 1115, 312);

		hPenOld = (HPEN)SelectObject(hdcMem, hPurpleLineThinPen);
		::MoveToEx(hdcMem, 1105, 327, NULL);
		::LineTo(hdcMem, 1115, 327);

		// draw report
		prc->top = 385;
		prc->left = 1100;
		sprintf_s(buffer, 50, "Results::");
		::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);

		for(int i = 5; i <= 100; i = i + 5){
			if(queue.nodesReceivedMessage / (float)MAX_NODE_NUMBER >= (i / (float)100))
			{
				prc->top = 400 + 3*i;
				
				
				if(timeHistory[(int)i/5] == 0)
					timeHistory[(int)i/5] = timeNow;
				sprintf_s(buffer, 50, "%d %% :: time: %d", i, timeHistory[(int)i/5]);
				::DrawTextA(hdcMem, buffer , -1, prc, DT_WORDBREAK);
			}
		}

		for(int i = 0; i < MAX_NODE_NUMBER; i++){
			index = sim.nodes[i].recipientList;

			// draw inactive connections
			if(sim.nodes[i].on){
				while(index != NULL){
					if(sim.getNode(index->nodeID).on){
						hPenOld = (HPEN)SelectObject(hdcMem, hGrayLineThinPen);
						::MoveToEx(hdcMem, (int) (sim.nodes[i].locationx * xExpansionRatio), (int) (sim.nodes[i].locationy * yExpansionRatio), NULL);
						::LineTo(hdcMem, (int) (sim.getNode(index->nodeID).locationx * xExpansionRatio), (int) (sim.getNode(index->nodeID).locationy * yExpansionRatio));
					}
					
					index = index->next;
				}
			}
		}

		for(int i = 0; i < MAX_NODE_NUMBER; i++){
			index = sim.nodes[i].recipientList;
			sender = sim.nodes[i];

			// draw the lines
			if(sender.on){
				while(index != NULL){
					recipient = sim.getNode(index->nodeID);
					if(recipient.on){
						if(sim.nodes[i].nodeStatus == SENT &&
							(recipient.nodeStatus == RECEIVING/* || recipient.nodeStatus == RECEIVED*/) &&
							recipient.message->senderID == sender.ID){
							switch(recipient.message->channelNumber){
							case 0:
								hPenOld = (HPEN)SelectObject(hdcMem, hRedLineThinPen);
								break;
							case 1:
								hPenOld = (HPEN)SelectObject(hdcMem, hYellowLineThinPen);
								break;
							case 2:
								hPenOld = (HPEN)SelectObject(hdcMem, hGreenLineThinPen);
								break;
							case 3:
								hPenOld = (HPEN)SelectObject(hdcMem, hBlueLineThinPen);
								break;
							case 4:
								hPenOld = (HPEN)SelectObject(hdcMem, hPurpleLineThinPen);
								break;
							}
							::MoveToEx(hdcMem, (int) (sim.nodes[i].locationx * xExpansionRatio), (int) (sim.nodes[i].locationy * yExpansionRatio), NULL);
							::LineTo(hdcMem, (int) (sim.getNode(index->nodeID).locationx * xExpansionRatio), (int) (sim.getNode(index->nodeID).locationy * yExpansionRatio));
						}
					}
					index = index->next;
				}
			}
		}

		// draw the nodes
		for(int i = 0; i < MAX_NODE_NUMBER; i++){
			if(sim.nodes[i].on){
				switch(sim.nodes[i].nodeStatus)
				{
				case NONE:
					hPenOld = (HPEN)SelectObject(hdcMem, hWhiteLineThickPen);
					break;
				case RECEIVING:
					hPenOld = (HPEN)SelectObject(hdcMem, hRedLineThickPen);
					break;
				case RECEIVED:
					hPenOld = (HPEN)SelectObject(hdcMem, hOrangeLineThickPen);
					break;
				case SENDING:
					hPenOld = (HPEN)SelectObject(hdcMem, hYellowLineThickPen);
					break;
				case SENT:
					hPenOld = (HPEN)SelectObject(hdcMem, hGreenLineThickPen);
					break;
				case RESENDABLE:
					hPenOld = (HPEN)SelectObject(hdcMem, hGreenLineThickPen);
					break;
				}
				::Ellipse(hdcMem, (int) (sim.nodes[i].locationx * xExpansionRatio - 2), (int) (sim.nodes[i].locationy * yExpansionRatio - 2), (int) (sim.nodes[i].locationx * xExpansionRatio + 3), (int) (sim.nodes[i].locationy * yExpansionRatio + 3));
			}else{
				hPenOld = (HPEN)SelectObject(hdcMem, hBlackLineThickPen);
				::Ellipse(hdcMem, (int) (sim.nodes[i].locationx * xExpansionRatio - 2), (int) (sim.nodes[i].locationy * yExpansionRatio - 2), (int) (sim.nodes[i].locationx * xExpansionRatio + 3), (int) (sim.nodes[i].locationy * yExpansionRatio + 3));
			}
		}

		BitBlt(hdc, 0, 0, DISPLAY_WINDOW_WIDTH, DISPLAY_WINDOW_WIDTH, hdcMem, 0, 0, SRCCOPY);

		SelectObject(hdcMem, hOld);
		DeleteObject(hbmMem);
		DeleteDC    (hdcMem);
		DeleteDC	(hdc);

		EndPaint(hWnd, &ps);
		return 0;
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}
